#include "sys.h"

color_selector cur_color;

color_selector user_color;

char cap_ascii[128] = {
    [0x0e] = 0x60, [0x15] = 0x51, [0x16] = 0x31, [0x1a] = 0x5a, [0x1b] = 0x53,
    [0x1c] = 0x41, [0x1d] = 0x57, [0x1e] = 0x32, [0x21] = 0x43, [0x22] = 0x58,
    [0x23] = 0x44, [0x24] = 0x45, [0x25] = 0x34, [0x26] = 0x33, [0x29] = 0x20,
    [0x2a] = 0x56, [0x2b] = 0x46, [0x2c] = 0x54, [0x2d] = 0x52, [0x2e] = 0x35,
    [0x31] = 0x4e, [0x32] = 0x42, [0x33] = 0x48, [0x34] = 0x47, [0x35] = 0x59,
    [0x36] = 0x36, [0x3a] = 0x4d, [0x3b] = 0x4a, [0x3c] = 0x55, [0x3d] = 0x37,
    [0x3e] = 0x38, [0x41] = 0x2c, [0x42] = 0x4b, [0x43] = 0x49, [0x44] = 0x4f,
    [0x45] = 0x30, [0x46] = 0x39, [0x49] = 0x2e, [0x4a] = 0x2f, [0x4b] = 0x4c,
    [0x4c] = 0x3b, [0x4d] = 0x50, [0x4e] = 0x2d, [0x52] = 0x27, [0x54] = 0x5b,
    [0x55] = 0x3d, [0x5a] = 0x0a, [0x5b] = 0x5d, [0x5d] = 0x5c, [0x66] = 0x08,
    [0x69] = 0x31, [0x6b] = 0x34, [0x6c] = 0x37, [0x70] = 0x30, [0x71] = 0x2e,
    [0x72] = 0x32, [0x73] = 0x35, [0x74] = 0x36, [0x75] = 0x38, [0x79] = 0x2b,
    [0x7a] = 0x33, [0x7b] = 0x2d, [0x7c] = 0x2a, [0x7d] = 0x39,
};

char cap_sig_ascii[128] = {
    [0x0e] = 0x7e, [0x15] = 0x51, [0x16] = 0x21, [0x1a] = 0x5a, [0x1b] = 0x53,
    [0x1c] = 0x41, [0x1d] = 0x57, [0x1e] = 0x40, [0x21] = 0x43, [0x22] = 0x58,
    [0x23] = 0x44, [0x24] = 0x45, [0x25] = 0x24, [0x26] = 0x23, [0x29] = 0x20,
    [0x2a] = 0x56, [0x2b] = 0x46, [0x2c] = 0x54, [0x2d] = 0x52, [0x2e] = 0x25,
    [0x31] = 0x4e, [0x32] = 0x42, [0x33] = 0x48, [0x34] = 0x47, [0x35] = 0x59,
    [0x36] = 0x5e, [0x3a] = 0x4d, [0x3b] = 0x4a, [0x3c] = 0x55, [0x3d] = 0x26,
    [0x3e] = 0x2a, [0x41] = 0x3c, [0x42] = 0x4b, [0x43] = 0x49, [0x44] = 0x4f,
    [0x45] = 0x29, [0x46] = 0x28, [0x49] = 0x3e, [0x4a] = 0x3f, [0x4b] = 0x4c,
    [0x4c] = 0x3a, [0x4d] = 0x50, [0x4e] = 0x5f, [0x52] = 0x22, [0x54] = 0x7b,
    [0x55] = 0x2b, [0x5a] = 0x0a, [0x5b] = 0x7d, [0x5d] = 0x7c, [0x66] = 0x08,
    [0x69] = 0x31, [0x6b] = 0x34, [0x6c] = 0x37, [0x70] = 0x30, [0x71] = 0x2e,
    [0x72] = 0x32, [0x73] = 0x35, [0x74] = 0x36, [0x75] = 0x38, [0x79] = 0x2b,
    [0x7a] = 0x33, [0x7b] = 0x2d, [0x7c] = 0x2a, [0x7d] = 0x39,
};

char normal_ascii[128] = {
    [0x0e] = 0x60, [0x15] = 0x71, [0x16] = 0x31, [0x1a] = 0x7a, [0x1b] = 0x73,
    [0x1c] = 0x61, [0x1d] = 0x77, [0x1e] = 0x32, [0x21] = 0x63, [0x22] = 0x78,
    [0x23] = 0x64, [0x24] = 0x65, [0x25] = 0x34, [0x26] = 0x33, [0x29] = 0x20,
    [0x2a] = 0x76, [0x2b] = 0x66, [0x2c] = 0x74, [0x2d] = 0x72, [0x2e] = 0x35,
    [0x31] = 0x6e, [0x32] = 0x62, [0x33] = 0x68, [0x34] = 0x67, [0x35] = 0x79,
    [0x36] = 0x36, [0x3a] = 0x6d, [0x3b] = 0x6a, [0x3c] = 0x75, [0x3d] = 0x37,
    [0x3e] = 0x38, [0x41] = 0x2c, [0x42] = 0x6b, [0x43] = 0x69, [0x44] = 0x6f,
    [0x45] = 0x30, [0x46] = 0x39, [0x49] = 0x2e, [0x4a] = 0x2f, [0x4b] = 0x6c,
    [0x4c] = 0x3b, [0x4d] = 0x70, [0x4e] = 0x2d, [0x52] = 0x27, [0x54] = 0x5b,
    [0x55] = 0x3d, [0x5a] = 0x0a, [0x5b] = 0x5d, [0x5d] = 0x5c, [0x66] = 0x08,
    [0x69] = 0x31, [0x6b] = 0x34, [0x6c] = 0x37, [0x70] = 0x30, [0x71] = 0x2e,
    [0x72] = 0x32, [0x73] = 0x35, [0x74] = 0x36, [0x75] = 0x38, [0x79] = 0x2b,
    [0x7a] = 0x33, [0x7b] = 0x2d, [0x7c] = 0x2a, [0x7d] = 0x39,
};

char sig_ascii[128] = {
    [0x0e] = 0x7e, [0x15] = 0x51, [0x16] = 0x21, [0x1a] = 0x5a, [0x1b] = 0x53,
    [0x1c] = 0x41, [0x1d] = 0x57, [0x1e] = 0x40, [0x21] = 0x43, [0x22] = 0x58,
    [0x23] = 0x44, [0x24] = 0x45, [0x25] = 0x24, [0x26] = 0x23, [0x29] = 0x20,
    [0x2a] = 0x56, [0x2b] = 0x46, [0x2c] = 0x54, [0x2d] = 0x52, [0x2e] = 0x25,
    [0x31] = 0x4e, [0x32] = 0x42, [0x33] = 0x48, [0x34] = 0x47, [0x35] = 0x59,
    [0x36] = 0x5e, [0x3a] = 0x4d, [0x3b] = 0x4a, [0x3c] = 0x55, [0x3d] = 0x26,
    [0x3e] = 0x2a, [0x41] = 0x3c, [0x42] = 0x4b, [0x43] = 0x49, [0x44] = 0x4f,
    [0x45] = 0x29, [0x46] = 0x28, [0x49] = 0x3e, [0x4a] = 0x3f, [0x4b] = 0x4c,
    [0x4c] = 0x3a, [0x4d] = 0x50, [0x4e] = 0x5f, [0x52] = 0x22, [0x54] = 0x7b,
    [0x55] = 0x2b, [0x5a] = 0x0a, [0x5b] = 0x7d, [0x5d] = 0x7c, [0x66] = 0x08,
    [0x69] = 0x31, [0x6b] = 0x34, [0x6c] = 0x37, [0x70] = 0x30, [0x71] = 0x2e,
    [0x72] = 0x32, [0x73] = 0x35, [0x74] = 0x36, [0x75] = 0x38, [0x79] = 0x2b,
    [0x7a] = 0x33, [0x7b] = 0x2d, [0x7c] = 0x2a, [0x7d] = 0x39,
};

char *vga_start = (char *)VGA_START;
char vga_line = 0;
char *base_line = (char *)VGA_LINE_O;
int vga_ch = 0;
unsigned char *key_buf = (unsigned char *)KEY_START;
volatile char *key_tail = (char *)KEY_TAIL;
volatile char *key_caps = (char *)KEY_CAPS;
volatile char *key_shift = (char *)KEY_SHIFT;
volatile char head = 0;
unsigned long long *time_head = (unsigned long long *)TIME_HEAD;
uint8_t *color_bucket = (uint8_t *)VGA_COLOR;
int *vga_mouse = (int *)VGA_MOUSE;
bool mouse_invisible = 0;
char unknown[] = "Error Input";
;

char *itoa(int i, char *a, int base) {
  if (i == 0) {
    a[0] = '0';
    a[1] = '\0';
    return a;
  }
  if (base == 16) {
    int cnt = 2;
    a[0] = '0';
    a[1] = 'x';
    while (i != 0) {
      int tmp = i & 0xf;
      if (tmp <= 9) {
        tmp = tmp + '0';
      } else {
        tmp = tmp - 10 + 'a';
      }
      a[cnt] = tmp;
      i >>= 4;
      ++cnt;
    }
    a[cnt] = '\0';
    int head = 2, tail = cnt - 1;
    while (head < tail) {
      int tmp = a[head];
      a[head] = a[tail];
      a[tail] = tmp;
      ++head;
      --tail;
    }
    return a;
  } else {
    int cnt = 0;
    while (i) {
      int tmp = __umodsi3(i, 10);
      tmp = tmp + '0';
      a[cnt++] = tmp;
      i = __udivsi3(i, 10);
    }
    a[cnt] = '\0';
    int head = 0, tail = cnt - 1;
    while (head < tail) {
      int tmp = a[head];
      a[head] = a[tail];
      a[tail] = tmp;
      ++head;
      --tail;
    }
    return a;
  }
}

int fib(int n) {
  int t1 = 0, t2 = 1;
  while (n--) {
    int tmp = t2;
    t2 = t1 + t2;
    t1 = tmp;
  }
  return t2;
}

struct KEY transform(int u) {
  struct KEY k;
  k.magic = 0;
  if (*key_shift && *key_caps) {
    k.c = cap_sig_ascii[u];
  } else if (*key_shift) {
    k.c = sig_ascii[u];
  } else if (*key_caps) {
    k.c = cap_ascii[u];
  } else
    k.c = normal_ascii[u];
  return k;
}

int atoi(char *a, int base) {
  int i = 0;
  if (base == 16) {
    while (*a != 0) {
      int tmp = *a;
      if (tmp <= '9') {
        tmp = tmp - '0';
      } else {
        tmp = tmp - 'a' + 10;
      }
      i = (i << 4) + tmp;
      ++a;
    }
  } else {
    while (*a) {
      int tmp = *a;
      tmp = tmp - '0';
      if (tmp < 0 || tmp > 9) {
        return i;
      }
      i = mul(i, base) + tmp;
      ++a;
    }
  }
  return i;
}

void vga_init() {
  (*base_line) = 0;
  vga_line = 0;
  vga_ch = 0;
  cur_color.fcolor = cur_color.bcolor = 0;
  user_color.fcolor = user_color.bcolor = 0;
  for (int i = 0; i < VGA_MAXLINE; i++)
    for (int j = 0; j < VGA_MAXCOL; j++) {
      set_color((i << 6) + (i << 2) + (i << 1) + j, cur_color);
      vga_start[(i << 6) + (i << 2) + (i << 1) + j] = 0;
    }
}

void clear_line(int lineno) {
  color_selector c;
  c.fcolor = c.bcolor = 0;
  for (int i = 0; i < VGA_MAXCOL; ++i) {
    set_color((lineno << 6) + (lineno << 2) + (lineno << 1) + i, c);
    vga_start[(lineno << 6) + (lineno << 2) + (lineno << 1) + i] = 0;
  }
}

void incre_head() {
  if (head == 15)
    head = 0;
  else
    ++head;
}

void vga_incre() {
  int place = (vga_line + *base_line) & LINE_MASK;
  vga_ch++;
  if (vga_ch >= VGA_MAXCOL) {
    ++vga_line;
    if (vga_line == VGA_SCREEN_LINE) {
      ++(*base_line);
      clear_line(*base_line - 1);
      if (*base_line == VGA_MAXLINE) {
        *base_line = 0;
      }
      --vga_line;
    }
    vga_ch = 0;
  }
  if (mouse_invisible)
    *vga_mouse = 114514;
  else
    *vga_mouse = ((place) << 6) + ((place) << 2) + ((place) << 1) + vga_ch;
  return;
}

KEY getch() {
  int c;
  volatile int not_ready = head == *key_tail;
  while (not_ready) {
    not_ready = head == *key_tail;
  };
  c = key_buf[head];
  KEY k;
  incre_head();
  if (c == 0x75) { // up pressed
    k.magic = -1;
    k.c = c;
  } else if (c == 0x72) { // down pressed
    k.magic = -1;
    k.c = c;
  } else if (c == 0x74) { // right pressed
    k.magic = -1;
    k.c = c;
  } else if (c == 0x6b) { // left pressed
    k.magic = -1;
    k.c = c;
  } else {
    k = transform(c);
  }
  return k;
}

int getr() {
  int c;
  while (head == *key_tail)
    ;
  do {
    c = key_buf[head];
  } while (c == 0x12 || c == 0x58);
  if (head == 15)
    head = 0;
  else
    ++head;
  return c;
}

void putch(char ch, int color) {

  int place = (vga_line + *base_line) & LINE_MASK;
  if (color) {
    set_color(((place) << 6) + ((place) << 2) + ((place) << 1) + vga_ch,
              cur_color);
  } else {
    set_color(((place) << 6) + ((place) << 2) + ((place) << 1) + vga_ch,
              user_color);
  }
  if (ch == 8) // backspace
  {
    if (vga_ch > 0) {
      vga_ch--;
    } else {
      if (vga_line > 0) {
        vga_line--;
        vga_ch = VGA_MAXCOL - 1;
      }
    }
  } else if (ch == 10) // enter
  {
    ++vga_line;
    if (vga_line == VGA_SCREEN_LINE) {
      ++(*base_line);
      clear_line(*base_line - 1);
      if (*base_line == VGA_MAXLINE) {
        *base_line = 0;
      }
      --vga_line;
    }
    vga_ch = 0;
  } else if (ch == '\t') { // a tab must be 2 spaces wide.
    putstr("  ", color);
  } else {
    vga_start[((place) << 6) + ((place) << 2) + ((place) << 1) + vga_ch] = ch;
    vga_ch++;
    if (vga_ch >= VGA_MAXCOL) {
      ++vga_line;
      if (vga_line == VGA_SCREEN_LINE) {
        ++(*base_line);
        clear_line(*base_line - 1);
        if (*base_line == VGA_MAXLINE) {
          *base_line = 0;
        }
        --vga_line;
      }
      vga_ch = 0;
    }
  }
  if (mouse_invisible)
    *vga_mouse = 114514;
  else
    *vga_mouse = ((place) << 6) + ((place) << 2) + ((place) << 1) + vga_ch;
  return;
}

void putstr(char *str, int color) {
  for (char *p = str; *p != 0; ++p)
    putch(*p, color);
}

void puts(char *str, int color) {
  putstr(str, color);
  putch('\n', color);
}

void gets(char *welcome1, char *welcome2, char *dest) {
  cur_color.fcolor = 2;
  cur_color.bcolor = 0;
  int place = (vga_line + *base_line) & LINE_MASK;
  int max_tail = 0;
  int cur_index = 0;
  putstr(welcome1, 1);
  cur_color.fcolor = 0;
  putstr(":", 1);
  cur_color.fcolor = 5;
  putstr(welcome2, 1);
  cur_color.fcolor = 0;
  cur_color.bcolor = 0;
  putstr("$ ", 1);
  int tail = 0;
  KEY c = getch();
  while (c.c != '\n') {
    if (c.magic == -1) {
      switch (c.c) {
      case 0x75: {
        break;
      }
      case 0x72: {
        break;
      }
      case 0x74: {
        if (dest[tail]) {
          tail++;
          vga_incre();
        }
        break;
      }
      case 0x6b: {
        if (tail > 0) {
          tail--;
          putch('\b', 0);
        }
        break;
      }
      }
      c = getch();
      continue;
    }
    if (c.c == 0x08) {
      if (tail > 0) {
        dest[--tail] = ' ';
        if (tail > max_tail)
          max_tail = tail;
        color_selector c = user_color;
        user_color.fcolor = user_color.bcolor = 0;
        putstr("\b \b", 0);
        user_color = c;
      }
    } else {
      dest[tail++] = c.c;
      if (tail > max_tail)
        max_tail = tail;
      putch(c.c, 0);
    }
    c = getch();
  }
  if (max_tail)
    putch('\n', 0);
  dest[max_tail] = '\0';
}

TIME time() {
  uint64_t time_now = *time_head;
  TIME t;
  t.val = time_now;
  t.ms = mod(time_now, 1000);
  time_now = div(time_now, 1000);
  t.s = mod(time_now, 60);
  time_now = div(time_now, 60);
  t.min = time_now;
  time_now = div(time_now, 60);
  t.hr = mod(time_now, 24);
  time_now = div(time_now, 24);
  t.day = time_now;
  return t;
}

void set_color(uint32_t addr, color_selector color) {
  color_bucket[addr] = color.val;
}